#ifndef __UNIT_H__
#define __UNIT_H__

#include <string>
#include <iostream>
#include <list>

class LengthUnit;
class VolumnUnit;
typedef std::list<LengthUnit> lengthUnitSets;
typedef std::list<VolumnUnit> volumnUnitSets;
typedef int UnitAmount;

class Unit
{
  public :
    virtual ~Unit() {};
    virtual UnitAmount convert2MinUnit(const UnitAmount &) const;
    virtual UnitAmount convertFromMinUnit(const UnitAmount&) const;
    virtual UnitAmount remainInMinUnit(const UnitAmount& amountInMinUnit) const;  

    virtual void formatUnitSets(std::ostream& out, const UnitAmount& amount) const = 0; 
    virtual const Unit& getMinUnit(void) const = 0;

    template <class T>
    void formatUnit(std::ostream& out, const UnitAmount& amount,T& unitSets) const;

    template <class T>
    void formatZeroUnit(std::ostream& out, T& unitSets) const;

    friend std::ostream& operator<<(std::ostream&,const Unit&);

  protected :
    Unit(const int&, const std::string&);

  private:
    const int factor_m;
    const std::string name_m;
};

class LengthUnit : public Unit
{
  public :
    static const LengthUnit& defineMILE();
    static const LengthUnit& defineYARD();
    static const LengthUnit& defineFEET();
    static const LengthUnit& minUnit(); 
    
    static lengthUnitSets& unitSet();
    void formatUnitSets(std::ostream& out, const UnitAmount& amount) const;
    const Unit& getMinUnit(void) const;
    
  private :
    LengthUnit(const int&, const std::string&);
};

class VolumnUnit : public Unit
{
  public :
    static const VolumnUnit& defineTSP();
    static const VolumnUnit& defineTBSP();
    static const VolumnUnit& defineOZ();
    static const VolumnUnit& minUnit(); 
    
    static volumnUnitSets& unitSet();
    void formatUnitSets(std::ostream& out, const UnitAmount& amount) const;
    const Unit& getMinUnit(void) const;
    
  private :
    VolumnUnit(const int&, const std::string&);
};

#define LENGTH Measure<LengthUnit> 
#define MILE LengthUnit::defineMILE()
#define YARD LengthUnit::defineYARD()
#define FEET LengthUnit::defineFEET()
#define LENGTH_MINUNIT LengthUnit::minUnit()
#define LENGTH_UNITSET LengthUnit::unitSet()

#define VOLUMN Measure<VolumnUnit>
#define TSP  VolumnUnit::defineTSP()
#define TBSP VolumnUnit::defineTBSP()
#define OZ   VolumnUnit::defineOZ()
#define VOLUMN_MINUNIT VolumnUnit::minUnit()
#define VOLUMN_UNITSET VolumnUnit::unitSet()

#endif

